
import initMounted from './mounted.js'
// import compiler from './CompilerRealDom.js'
import { compileToFunction } from "./Compiler.js";
import {nextTick} from "./watchAndDep/nextTick.js";
import {isObj, isVNode, isUndef, isEle} from "./utils.js";
import Watcher from './watchAndDep/Watcher.js'
import { VNode, createEmptyVnode, renderRealDomByVnode} from "./vnode/main.js";
import {isStr} from "./utils.js";
import { setActiveInstance } from './vnode/ActiveInstance.js'

import initMixin from './mixins/initMixin.js'
import stateMixin from './mixins/stateMixin.js'
import eventsMixin from './mixins/eventsMixin.js'
import lifecycleMixin from './mixins/lifecycleMixin.js'
import renderMixin from './mixins/renderMixin.js'
import { updateRealDomByPatches, diffVnode } from './vnode/diff.js'






// function initComputed(vm){
//     let watchers = vm._watchComputed = Object.create(vm.$options['computed'])
//     doCallback(vm, 'computed', (key , val)=>{
//         vm.$computed(key, val)
//     })
// }

class Vue {
    constructor(options){
        let vm = this


        vm._init(options)

        // // 初始化data对象中的数据， 双向数据绑定
        //initState(vm.$data)


        // // initComputed(vm)
        //
        // 初始化更新和挂载视图
        //initMounted(vm)
    }
    // 更新视图方法， 此方法 实际实现因该是 AST->VNODE ->DIFF ->PATCH  此处节省时间， 直接编译dom
    _update(vnode, status){
        let vm = this
        // 每次编译dom中的数据， 都会将整个dom都重新修改一次  此方法主要 1， 获取dom中的 变量， 2，替换变量 3， 赋值给 dom根节点
        let prevEle = vm.$el
        let prevVnode = vm._vnode // 原来的vnode
        let restoreActiveInstance = setActiveInstance(vm)  // 将当前的 ActiveInstance 指向当前的 vm实例， 为的是在 patch时 获取对应的变量
        vm._vnode = vnode // 现在的 vnode
        if(!prevVnode){
            // 说明原来没有， 说明是 初始化
            vm.__patch__(vm.$el, vnode);
        }else{
            // 说明已有vnode， 是更新
            vm.__patch__(prevVnode, vnode);
        }

        restoreActiveInstance() // 还原当前ActiveInstance的指向 // 这种高阶函数的用法在 Vue3中 多用于实现 step 也类似于 React Hooks
    }
    $forceUpdate(){
        var vm = this;
        if (vm._watcher) {
            vm._watcher.update();
        }
    }
    _render(){
        let vm = this
        let vnode = null
        let options = vm.$options

        if(options.render && typeof options.render === 'function'){
            let res = options.render()
            vnode = isVNode(res) ? res : createEmptyVnode();
        }else{
            let render;
            if(options.template && isStr(options.template) && options.template.chartAt(0) !== '#'){
                render = compileToFunction(options.template).render
            }else{
                render = compileToFunction(vm.$el.outerHTML).render;
            }
            vnode = render.call(vm);
        }
        if(!isVNode(vnode)){
            throw new TypeError('无法挂载实例， 请查证后再试')
        }

        return vnode
    }
    __patch__(oldVnode, vnode){
        let ele = null
        if(isEle(oldVnode)){
            ele = oldVnode
            ele.innerHTML = ''
            let html = renderRealDomByVnode(vnode)
            Array.from(html.childNodes).forEach(node => {
                ele.appendChild(node)
            })
        }else{
            ele = this.$el
            let patches = diffVnode(oldVnode, vnode)
            updateRealDomByPatches(ele, patches)
        }

        return ele
    }
    // 每次同步代码中，相关数据更新完毕后， 执行 new Watcher 的 update方法， 此方法将每一个 fn都保存起来， 在所有 数据更新后， 一起异步执行
    $nextTick(fn){
        return nextTick(fn)
    }
    $watch(key, handler){
        new Watcher(this, key, handler)
    }

    _v(str){
        return new VNode(undefined, undefined, str);
    }

    _s(str){

        return typeof str === 'object' ?
            JSON.stringify(str, null, 2)
            : str;
    }

    // $computed(key, handler){
    //     new Watcher(this, key , ()=>{} , {lazy: true})
    // }
}


initMixin(Vue);
stateMixin(Vue);
eventsMixin(Vue);
lifecycleMixin(Vue);
renderMixin(Vue);

export default Vue

